package compile;

import jdk.nashorn.internal.parser.TokenKind;

/**
 * @author SongJun
 * @description 词法分析器
 * @date 2021/9/28 14:47
 */
public class Tokenizer {
    private CharStream stream;
    Token nextToken = new Token(TokenKindEnum.EOF, "");

    public Tokenizer(CharStream stream) {
        this.stream = stream;
    }

    public Token next(){
        //在第一次的时候，先parse一个Token
        if(this.nextToken.kind.equals(TokenKindEnum.EOF) && !this.stream.eof()){
            this.nextToken = this.getAToken();
        }
        Token lastToken = this.nextToken;

        //往前走一个Token
        this.nextToken = this.getAToken();
        return lastToken;
    }

    public Token peek(){
        if(!this.stream.eof()){
            this.nextToken = this.getAToken();
        }
        return this.nextToken;
    }

    private Token getAToken(){
        this.skipWhiteSpaces();
        if (this.stream.eof()){
            return new Token(TokenKindEnum.EOF, "");
        }
        else{
            char ch = this.stream.peek();
            if (this.isLetter(ch) || this.isDigit(ch)){
                return this.parseIdentifer();
            }
            else if (ch == '"'){
                return this.parseStringLiteral();
            }
            else if (ch == '(' || ch == ')' || ch == '{' ||
                    ch == '}' || ch == ';' || ch == ','){
                this.stream.next();
                return new Token(TokenKindEnum.Seperator, ""+ch);
            } else if (ch == '/'){
                this.stream.next();
                char ch1 = this.stream.peek();
                if (ch1 == '*'){
                    this.skipMultipleLineComments();
                    return this.getAToken();
                }
                else if (ch1 == '/'){
                    this.skipSingleLineComment();
                    return this.getAToken();
                }
                else if (ch1 == '='){
                    this.stream.next();
                    return new Token(TokenKindEnum.Operator, "/=");
                }
                else{
                    return new Token(TokenKindEnum.Operator, "/");
                }
            } else if (ch == '+'){
                this.stream.next();
                char ch1 = this.stream.peek();
                if (ch1 == '+'){
                    this.stream.next();
                    return new Token(TokenKindEnum.Operator, "++");
                } else if (ch1 == '='){
                    this.stream.next();
                    return new Token(TokenKindEnum.Operator, "+=");

                } else{
                    return new Token(TokenKindEnum.Operator, "+");
                }
            } else if (ch == '-'){
                this.stream.next();
                char ch1 = this.stream.peek();
                if (ch1 == '-'){
                    this.stream.next();
                    return new Token(TokenKindEnum.Operator, "--");
                }else if (ch1 == '='){
                    this.stream.next();
                    return new Token(TokenKindEnum.Operator, "-=");
                }
                else{
                    return new Token(TokenKindEnum.Operator, "-");
                }
            } else if (ch == '*'){
                this.stream.next();
                char ch1 = this.stream.peek();
                if (ch1 == '='){
                    this.stream.next();
                    return new Token(TokenKindEnum.Operator, "*=");
                }
                else{
                    return new Token(TokenKindEnum.Operator, "*");
                }
            } else{
                //暂时去掉不能识别的字符
                System.out.println("Unrecognized pattern meeting ': " +ch+"', at" + this.stream.line + " col: " + this.stream.col);
                this.stream.next();
                return this.getAToken();
            }
        }
    }


    /**
     * 跳过单行注释
     */
    private void skipSingleLineComment(){
        //跳过第二个/，第一个之前已经跳过去了。
        this.stream.next();

        //往后一直找到回车或者eof
        while(this.stream.peek() !='\n' && !this.stream.eof()){
            this.stream.next();
        }
    }

    /**
     * 跳过多行注释
     */
    private void skipMultipleLineComments(){
        //跳过*，/之前已经跳过去了。
        this.stream.next();

        if (!this.stream.eof()){
            char ch1 = this.stream.next();
            //往后一直找到回车或者eof
            while(!this.stream.eof()){
                char ch2 = this.stream.next();
                if (ch1 == '*' && ch2 == '/'){
                    return;
                }
                ch1 = ch2;
            }
        }

        //如果没有匹配上，报错。
        System.out.println("Failed to find matching */ for multiple line comments at ': " + this.stream.line + " col: " + this.stream.col);
    }

    /**
     * 跳过空白字符
     */
    private void skipWhiteSpaces(){
        while (!this.stream.eof() && this.isWhiteSpace(this.stream.peek())){
            this.stream.next();
        }
    }

    /**
     * 字符串字面量。
     * 目前只支持双引号，并且不支持转义。
     */
    private Token parseStringLiteral(){
        Token token = new Token(TokenKindEnum.StringLiteral, "");

        //第一个字符不用判断，因为在调用者那里已经判断过了
        this.stream.next();

        while(!this.stream.eof() && this.stream.peek() !='"'){
            token.text+=this.stream.next();
        }

        if(this.stream.peek()=='"'){
            //消化掉字符换末尾的引号
            this.stream.next();
        }
        else{
            System.out.println("Expecting an \" at line: " + this.stream.line + " col: " + this.stream.col);
        }
        return token;
    }

    /**
     * 解析标识符。从标识符中还要挑出关键字。
     */
    private Token parseIdentifer(){
        Token token = new Token(TokenKindEnum.Identifier, "");

        //第一个字符不用判断，因为在调用者那里已经判断过了
        token.text+=this.stream.next();

        //读入后序字符
        while(!this.stream.eof() &&
                this.isLetterDigitOrUnderScore(this.stream.peek())){
            token.text += this.stream.next();
        }

        //识别出关键字
        if (token.text.equals("function")){
            token.kind = TokenKindEnum.Keyword;
        }

        return token;
    }

    private boolean isLetterDigitOrUnderScore(char ch){
        return (ch>='A' && ch<='Z' ||
                ch>='a' && ch<='z' ||
                ch>='0' && ch<='9' ||
                ch== '_');
    }

    private boolean isLetter(char ch){
        return (ch>='A' && ch <='Z' || ch>= 'a' && ch <='z');
    }

    private boolean isDigit(char ch){
        return (ch>='0' && ch <='9');
    }

    private boolean isWhiteSpace(char ch){
        return (ch == ' ' || ch == '\n' || ch== '\t');
    }




}
